/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */
#include "ProportionalTextImpl.h"
#include "font/Proportional.h"
#include "TextRenderInfo.h"
#include <lib3d/CameraDesc.h>
#include <system/assert.h>	
#include <math/utl.h>

namespace liba {
namespace lib3d {
namespace node {

ProportionalTextImpl::ProportionalTextImpl(font::Proportional* font)
	: font(font)
{
	hard::Manager::add_dependent( this );
}

ProportionalTextImpl::ProportionalTextImpl(const ProportionalTextImpl & other) : TextImpl(other), font(font->clone())
{
	hard::Manager::add_dependent( this );
}
	
ProportionalTextImpl::~ProportionalTextImpl()
{
	hard::Manager::remove_dependent( this );
}
	
ProportionalTextImpl * ProportionalTextImpl::clone()
{
	return new ProportionalTextImpl(*this);
}

void ProportionalTextImpl::change_valignment(VAlignment value)
{
	LIBA_FAIL("Not implemented");
}

void ProportionalTextImpl::on_add_hardware(hard::Manager::hardware_type * ha)
{
	hard_vector.push_back( SweetPair(ha, ha->create_vertex_buffer()) );
}

void ProportionalTextImpl::on_change_text()
{
	double koef = 1.0;
	if(GetTextData()->GetNormalize())
	{
		const double line_shift = double( font->get_line_shift() ) / font->get_height();
		koef = 1.0/(font->get_width() * line_shift);
	}

	double base = double( font->get_base() );
	if(GetTextData()->GetNormalize())
		base /= double( font->get_line_shift() );

	SimpleArray<float> positions(6 * GetTextData()->GetText().size() * 3);
	SimpleArray<float> uvs(6 * GetTextData()->GetText().size() * 2);
	float * pos_data = positions.data();
	float * uvs_data = uvs.data();

	double width_x = 0;
	for( int l = 0; l != GetTextData()->GetText().size(); ++l )
	{
		const font::Proportional::Letter & letter = font->get_letter( GetTextData()->GetText()[l] );
		width_x += letter.base_width * koef;
	}

	float current_x = 0; // default:
	switch( GetTextData()->GetAlignment() )
	{
	case TextData::CENTER:
		current_x = width_x/2;
		break;
	case TextData::RIGHT:
		current_x = width_x;
		break;
	}
	float current_y = -1;
	for( int l = 0; l != GetTextData()->GetText().size(); ++l )
	{
		const font::Proportional::Letter & letter = font->get_letter( GetTextData()->GetText()[l] );
		const float dx = -float( letter.width ) * koef;
		const float dy = float( letter.height ) * koef;
		const float x = float( current_x + letter.base_skip * koef );
		const float y = float( current_y - base );

		const float du = float( letter.width - 1 ) / font->get_width();
		const float dv = float( letter.height - 1 ) / font->get_height();
		const float u = float( letter.x + 0.5 ) / font->get_width();
		const float v = 1 - float( letter.y + 0.5 ) / font->get_height() - dv;

		*pos_data++ = x;
		*pos_data++ = y;
		*pos_data++ = 0;
		*uvs_data++ = u;
		*uvs_data++ = v;

		*pos_data++ = x;
		*pos_data++ = y + dy;
		*pos_data++ = 0;
		*uvs_data++ = u;
		*uvs_data++ = v + dv;

		*pos_data++ = x + dx;
		*pos_data++ = y;
		*pos_data++ = 0;
		*uvs_data++ = u + du;
		*uvs_data++ = v;

		*pos_data++ = x + dx;
		*pos_data++ = y;
		*pos_data++ = 0;
		*uvs_data++ = u + du;
		*uvs_data++ = v;

		*pos_data++ = x;
		*pos_data++ = y + dy;
		*pos_data++ = 0;
		*uvs_data++ = u;
		*uvs_data++ = v + dv;

		*pos_data++ = x + dx;
		*pos_data++ = y + dy;
		*pos_data++ = 0;
		*uvs_data++ = u + du;
		*uvs_data++ = v + dv;

		current_x -= float(letter.base_width * koef);
	}

	SimpleArray<float> empty_float;
	SimpleArray<int> empty_int;

	HardVector::iterator it = hard_vector.begin();
	for( ;it != hard_vector.end(); ++it )
	{
		if( !it->second->update( positions, empty_float, empty_float, uvs, uvs) )
		{
			THROW(Gluck(__FUNCTION__, "it->second->update() failed"));
		}
	}
}

void ProportionalTextImpl::render_to_list( hard::Hardware * hardware, TextRenderInfo* tri, hard::RenderList * render_list )
{
	hard::RenderModel render_model;

	render_model.material.diffuse = tri->color;
	render_model.material.emissive = tri->color;
	tri->transparency = math::clamp( tri->transparency, 0.0001f, 0.9999f );
	render_model.material.transparency = tri->transparency;

	render_model.world = tri->total_matrix;

	hard::VertexBuffer * vb = find_data(hardware);
	if( !vb->is_valid() )
		on_change_text();

	render_model.vb = vb;
	render_model.primitive_start = 0;
	render_model.primitive_count = GetTextData()->GetText().size() * 2;
	render_model.ib = 0;
	render_model.vertex_start = 0;
	render_model.vertex_count = GetTextData()->GetText().size() * 6;

	render_list->models.push_back( render_model );
}

float ProportionalTextImpl::get_text_width(const std::wstring & text)
{
	double koef = 1.0;
	if(GetTextData()->GetNormalize())
	{
		const double line_shift = double( font->get_line_shift() ) / font->get_height();
		koef = 1.0 / (font->get_width() * line_shift);
	}

	double width_x = 0;
	for( int l = 0; l != text.size(); ++l )
	{
		const font::Proportional::Letter & letter = font->get_letter( text[l] );
		width_x += letter.base_width * koef;
	}

	return width_x;
}
	
float ProportionalTextImpl::get_text_height()
{
	int lines = 1;
	
	for(unsigned int i = 0; i < GetTextData()->GetText().size(); i++)
	{
		if(GetTextData()->GetText()[i] == '\n')
			lines++;
	}
	
	return font->get_height() * lines;
}

void ProportionalTextImpl::text_data_changed()
{
	on_change_text();
}

} // namespace node
} // namespace lib3d
} // namespace liba